Build a Flutter Weather App
A Flutter Weather App is a practical project that demonstrates how to build a real-world application that retrieves weather information from an online API and displays it in a user-friendly interface. This project helps you understand Flutter UI development, HTTP requests, JSON parsing, asynchronous programming, API integration, state management, loading states, error handling, and responsive UI design.
In this project, the user can enter a city name, request weather information from a weather API, and display details such as temperature, weather condition, humidity, wind speed, and other useful information.
1. What is a Weather App?
A Weather App is an application that retrieves weather information from a weather service and presents the information to the user.
A typical weather application can display:
- City name
- Current temperature
- Weather condition
- Feels-like temperature
- Humidity
- Wind speed
- Pressure
- Visibility
- Weather icon
- Forecast information
2. Objectives of the Project
After completing this project, you will understand how to:
- Create a Flutter Weather App.
- Create a user-friendly weather interface.
- Accept a city name using TextField.
- Make HTTP GET requests.
- Connect Flutter with a REST API.
- Work with JSON data.
- Create Dart model classes.
- Use Future and async/await.
- Handle loading states.
- Handle API errors.
- Display dynamic weather information.
- Use setState() for simple state management.
- Work with API keys securely at an appropriate application architecture level.
- Create reusable widgets.
3. Technologies Used
| Technology | Purpose |
| Flutter | Builds the cross-platform user interface |
| Dart | Programming language used by Flutter |
| Material Design | Provides Flutter UI components |
| HTTP | Makes network requests to the weather API |
| JSON | Represents weather data returned by the API |
| Future | Represents asynchronous operations |
| async/await | Handles asynchronous API operations |
| StatefulWidget | Manages changing application state |
4. Basic Weather App Flow
User opens Weather App
↓
Weather screen appears
↓
User enters city name
↓
User presses Search
↓
Flutter sends HTTP request
↓
Weather API processes request
↓
API returns JSON response
↓
Flutter decodes JSON
↓
Weather model is created
↓
UI displays weather information
5. Understanding API Integration
An API allows the Flutter application to communicate with an external service. For a weather application, the Flutter app sends a request containing information such as the city name or geographic coordinates. The weather service processes the request and returns weather data.
Flutter App
↓
HTTP Request
↓
Weather API
↓
JSON Response
↓
Dart Model
↓
Flutter UI
Flutter's official documentation recommends the http package as a simple way to make HTTP requests. :contentReference[oaicite:0]{index=0}
6. Create a Flutter Project
Create a new Flutter project using the Flutter CLI.
flutter create weather_app
Move into the project directory:
cd weather_app
Run the application:
flutter run
7. Add the HTTP Package
The http package can be added using the Flutter command:
flutter pub add http
Import it into your Dart file:
import 'package:http/http.dart' as http;
The Flutter networking documentation uses this package for making HTTP requests and fetching data from the internet. :contentReference[oaicite:1]{index=1}
8. Android Internet Permission
When deploying an Android application that accesses the internet, the Android manifest needs the Internet permission.
This permission should be placed in the appropriate Android manifest file. Flutter's networking documentation specifically notes this requirement for Android network access. :contentReference[oaicite:2]{index=2}
9. Project Structure
weather_app/
├── android/
├── ios/
├── lib/
│ ├── main.dart
│ ├── models/
│ │ └── weather.dart
│ ├── services/
│ │ └── weather_service.dart
│ └── widgets/
│ └── weather_card.dart
├── test/
├── web/
├── pubspec.yaml
└── README.md
For a beginner project, the entire application can initially be created in main.dart. As the project grows, separating models, services, and widgets makes the code easier to maintain.
10. Create the Main Function
void main() {
runApp(const WeatherApp());
}
The main() function is the starting point of the Dart application. The runApp() function places the root widget into the Flutter widget tree.
11. Create the Root Application
class WeatherApp extends StatelessWidget {
const WeatherApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Weather App',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(
seedColor: Colors.blue,
),
useMaterial3: true,
),
home: const WeatherPage(),
);
}
}
12. Why StatefulWidget is Useful
The weather screen needs to change when the user searches for a city. The application can move between states such as loading, successful data, and error.
A StatefulWidget is therefore useful for a simple implementation.
class WeatherPage extends StatefulWidget {
const WeatherPage({super.key});
@override
State createState() => _WeatherPageState();
}
13. Create a Weather Model
Instead of working directly with a large JSON map throughout the UI, create a Dart class that represents the weather data required by the application.
class Weather {
final String city;
final double temperature;
final double feelsLike;
final int humidity;
final double windSpeed;
final String description;
Weather({
required this.city,
required this.temperature,
required this.feelsLike,
required this.humidity,
required this.windSpeed,
required this.description,
});
factory Weather.fromJson(Map json) {
return Weather(
city: json['name'] ?? '',
temperature:
(json['main']['temp'] as num).toDouble(),
feelsLike:
(json['main']['feels_like'] as num).toDouble(),
humidity: json['main']['humidity'] ?? 0,
windSpeed:
(json['wind']['speed'] as num).toDouble(),
description:
json['weather'][0]['description'] ?? '',
);
}
}
14. Understanding JSON
Weather APIs generally return structured data in JSON format. The JSON response may contain objects and arrays containing information about the city, temperature, humidity, wind, and weather conditions.
A simplified response might look like this:
{
"name": "Mumbai",
"main": {
"temp": 28.5,
"feels_like": 30.1,
"humidity": 75
},
"weather": [
{
"description": "clear sky"
}
],
"wind": {
"speed": 4.2
}
}
15. JSON Parsing
Dart provides dart:convert for decoding JSON responses.
import 'dart:convert';
final data = jsonDecode(response.body);
The decoded JSON can then be converted into a Dart object.
final weather = Weather.fromJson(
jsonDecode(response.body),
);
Flutter's networking examples demonstrate decoding an HTTP response body using jsonDecode() and converting the result into a Dart model. :contentReference[oaicite:3]{index=3}
16. Create a Weather Service
It is good practice to separate API communication from the UI. Create a service class responsible for requesting weather data.
import 'dart:convert';
import 'package:http/http.dart' as http;
class WeatherService {
final String apiKey;
WeatherService(this.apiKey);
Future fetchWeather(String city) async {
final url = Uri.parse(
'https://api.example.com/weather'
'?city=${Uri.encodeComponent(city)}'
'&key=$apiKey',
);
final response = await http.get(url);
if (response.statusCode == 200) {
final data =
jsonDecode(response.body) as Map;
return Weather.fromJson(data);
}
throw Exception('Failed to load weather data');
}
}
The exact endpoint, parameters, response fields, and authentication method depend on the weather API provider you choose.
17. HTTP GET Request
A GET request is commonly used to retrieve weather information.
final response = await http.get(
Uri.parse(url),
);
The request returns a Future, because network communication happens asynchronously. :contentReference[oaicite:4]{index=4}
18. Understanding async and await
Network requests can take time. Flutter should not block the UI while waiting for a response.
Future fetchWeather(String city) async {
final response = await http.get(
Uri.parse(url),
);
// Process response
}
async allows the function to perform asynchronous work, while await waits for a Future to complete before continuing that function.
19. HTTP Status Codes
| Status Code | Meaning | Possible App Action |
| 200 | Success | Parse and display weather |
| 400 | Bad Request | Check request parameters |
| 401 | Unauthorized | Check API credentials |
| 404 | Not Found | Show city/data not found message |
| 429 | Too Many Requests | Handle rate-limit condition |
| 500 | Server Error | Show server error message |
20. Create TextEditingController
The user needs a way to enter the city name. TextEditingController can be used to read the input.
final TextEditingController _cityController =
TextEditingController();
The entered city can be accessed using:
final city = _cityController.text.trim();
21. Dispose the Controller
When the State object is removed, the controller should be disposed.
@override
void dispose() {
_cityController.dispose();
super.dispose();
}
22. Create Weather App State Variables
Weather? _weather;
bool _isLoading = false;
String? _errorMessage;
These variables can represent three basic states:
| State | Meaning |
| Initial | No weather has been searched yet |
| Loading | API request is in progress |
| Success | Weather data was received |
| Error | Something went wrong while loading data |
23. Create the Search Method
Future _searchWeather() async {
final city = _cityController.text.trim();
if (city.isEmpty) {
setState(() {
_errorMessage = 'Please enter a city name.';
});
return;
}
setState(() {
_isLoading = true;
_errorMessage = null;
});
try {
final weather =
await _weatherService.fetchWeather(city);
setState(() {
_weather = weather;
_isLoading = false;
});
} catch (e) {
setState(() {
_isLoading = false;
_errorMessage =
'Unable to load weather information.';
});
}
}
24. Search Flow
Search Button
↓
Read city name
↓
Validate input
↓
Set loading = true
↓
Call Weather API
↓
Receive response
↓
Decode JSON
↓
Create Weather object
↓
Update state
↓
Display weather
25. Build the Search Field
TextField(
controller: _cityController,
textInputAction: TextInputAction.search,
decoration: const InputDecoration(
hintText: 'Enter city name',
prefixIcon: Icon(Icons.location_city),
border: OutlineInputBorder(),
),
onSubmitted: (_) {
_searchWeather();
},
)
26. Create Search Button
ElevatedButton.icon(
onPressed: _isLoading
? null
: _searchWeather,
icon: const Icon(Icons.search),
label: const Text('Search'),
)
Disabling the button while loading helps prevent multiple requests from being sent at the same time.
27. Display Loading State
if (_isLoading)
const Center(
child: CircularProgressIndicator(),
)
A loading indicator gives the user feedback while the API request is being processed.
28. Display Error State
if (_errorMessage != null)
Text(
_errorMessage!,
style: const TextStyle(
color: Colors.red,
),
)
Error messages should be clear and useful rather than exposing raw technical exceptions to the user.
29. Display Weather Data
Column(
children: [
Text(
_weather!.city,
style: const TextStyle(
fontSize: 28,
fontWeight: FontWeight.bold,
),
),
Text(
'${_weather!.temperature.toStringAsFixed(1)}°C',
style: const TextStyle(
fontSize: 48,
fontWeight: FontWeight.bold,
),
),
Text(
_weather!.description,
),
],
)
30. Weather Information Card
Card(
child: Padding(
padding: const EdgeInsets.all(20),
child: Column(
children: [
Text(
_weather!.city,
style: const TextStyle(
fontSize: 28,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 12),
Text(
'${_weather!.temperature.toStringAsFixed(1)}°C',
style: const TextStyle(
fontSize: 50,
fontWeight: FontWeight.bold,
),
),
Text(
_weather!.description,
style: const TextStyle(
fontSize: 18,
),
),
],
),
),
)
31. Display Humidity
WeatherInfoItem(
icon: Icons.water_drop,
label: 'Humidity',
value: '${_weather!.humidity}%',
)
32. Display Wind Speed
WeatherInfoItem(
icon: Icons.air,
label: 'Wind',
value: '${_weather!.windSpeed} m/s',
)
33. Reusable Weather Information Widget
class WeatherInfoItem extends StatelessWidget {
final IconData icon;
final String label;
final String value;
const WeatherInfoItem({
super.key,
required this.icon,
required this.label,
required this.value,
});
@override
Widget build(BuildContext context) {
return Column(
children: [
Icon(icon, size: 30),
const SizedBox(height: 6),
Text(label),
const SizedBox(height: 4),
Text(
value,
style: const TextStyle(
fontWeight: FontWeight.bold,
),
),
],
);
}
}
34. Display Multiple Weather Details
Row(
mainAxisAlignment:
MainAxisAlignment.spaceAround,
children: [
WeatherInfoItem(
icon: Icons.water_drop,
label: 'Humidity',
value: '${_weather!.humidity}%',
),
WeatherInfoItem(
icon: Icons.air,
label: 'Wind',
value: '${_weather!.windSpeed} m/s',
),
WeatherInfoItem(
icon: Icons.thermostat,
label: 'Feels Like',
value:
'${_weather!.feelsLike.toStringAsFixed(1)}°C',
),
],
)
35. Complete Weather UI Structure
Scaffold
├── AppBar
│ └── Text
│
└── Body
└── SafeArea
└── Padding
└── Column
├── TextField
├── Search Button
├── Loading Indicator
├── Error Message
└── Weather Card
├── City
├── Temperature
├── Description
└── Weather Details
├── Humidity
├── Wind
└── Feels Like
36. Complete Flutter Weather App Example
The following example demonstrates the application structure. Replace the example API endpoint and response fields with those provided by your selected weather API service.
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
void main() {
runApp(const WeatherApp());
}
class Weather {
final String city;
final double temperature;
final double feelsLike;
final int humidity;
final double windSpeed;
final String description;
Weather({
required this.city,
required this.temperature,
required this.feelsLike,
required this.humidity,
required this.windSpeed,
required this.description,
});
factory Weather.fromJson(
Map json,
) {
return Weather(
city: json['name'] ?? '',
temperature:
(json['main']['temp'] as num).toDouble(),
feelsLike:
(json['main']['feels_like'] as num).toDouble(),
humidity: json['main']['humidity'] ?? 0,
windSpeed:
(json['wind']['speed'] as num).toDouble(),
description:
json['weather'][0]['description'] ?? '',
);
}
}
class WeatherService {
final String apiKey;
WeatherService(this.apiKey);
Future fetchWeather(
String city,
) async {
final url = Uri.parse(
'https://api.example.com/weather'
'?city=${Uri.encodeComponent(city)}'
'&key=$apiKey',
);
final response = await http.get(url);
if (response.statusCode == 200) {
final data =
jsonDecode(response.body)
as Map;
return Weather.fromJson(data);
}
throw Exception(
'Failed to load weather data',
);
}
}
class WeatherApp extends StatelessWidget {
const WeatherApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Weather App',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(
seedColor: Colors.blue,
),
useMaterial3: true,
),
home: const WeatherPage(),
);
}
}
class WeatherPage extends StatefulWidget {
const WeatherPage({super.key});
@override
State createState() =>
_WeatherPageState();
}
class _WeatherPageState
extends State {
final TextEditingController _cityController =
TextEditingController();
final WeatherService _weatherService =
WeatherService('YOUR_API_KEY');
Weather? _weather;
bool _isLoading = false;
String? _errorMessage;
Future _searchWeather() async {
final city = _cityController.text.trim();
if (city.isEmpty) {
setState(() {
_errorMessage =
'Please enter a city name.';
});
return;
}
setState(() {
_isLoading = true;
_errorMessage = null;
});
try {
final weather =
await _weatherService.fetchWeather(city);
setState(() {
_weather = weather;
_isLoading = false;
});
} catch (e) {
setState(() {
_isLoading = false;
_errorMessage =
'Unable to load weather information.';
});
}
}
@override
void dispose() {
_cityController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Weather App'),
centerTitle: true,
),
body: SafeArea(
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
children: [
TextField(
controller: _cityController,
textInputAction:
TextInputAction.search,
decoration: const InputDecoration(
hintText: 'Enter city name',
prefixIcon:
Icon(Icons.location_city),
border: OutlineInputBorder(),
),
onSubmitted: (_) {
_searchWeather();
},
),
const SizedBox(height: 12),
SizedBox(
width: double.infinity,
child: ElevatedButton.icon(
onPressed: _isLoading
? null
: _searchWeather,
icon: const Icon(Icons.search),
label: const Text('Search'),
),
),
const SizedBox(height: 24),
if (_isLoading)
const CircularProgressIndicator(),
if (_errorMessage != null)
Padding(
padding:
const EdgeInsets.only(top: 16),
child: Text(
_errorMessage!,
textAlign: TextAlign.center,
style: const TextStyle(
color: Colors.red,
),
),
),
if (_weather != null &&
!_isLoading)
Expanded(
child: SingleChildScrollView(
child: Card(
child: Padding(
padding:
const EdgeInsets.all(20),
child: Column(
children: [
Text(
_weather!.city,
style: const TextStyle(
fontSize: 28,
fontWeight:
FontWeight.bold,
),
),
const SizedBox(
height: 16,
),
Text(
'${_weather!.temperature.toStringAsFixed(1)}°C',
style:
const TextStyle(
fontSize: 48,
fontWeight:
FontWeight.bold,
),
),
const SizedBox(
height: 8,
),
Text(
_weather!.description,
style:
const TextStyle(
fontSize: 18,
),
),
const SizedBox(
height: 24,
),
Row(
mainAxisAlignment:
MainAxisAlignment
.spaceAround,
children: [
WeatherInfoItem(
icon: Icons.water_drop,
label: 'Humidity',
value:
'${_weather!.humidity}%',
),
WeatherInfoItem(
icon: Icons.air,
label: 'Wind',
value:
'${_weather!.windSpeed} m/s',
),
WeatherInfoItem(
icon:
Icons.thermostat,
label: 'Feels Like',
value:
'${_weather!.feelsLike.toStringAsFixed(1)}°C',
),
],
),
],
),
),
),
),
),
],
),
),
),
);
}
}
class WeatherInfoItem
extends StatelessWidget {
final IconData icon;
final String label;
final String value;
const WeatherInfoItem({
super.key,
required this.icon,
required this.label,
required this.value,
});
@override
Widget build(BuildContext context) {
return Column(
children: [
Icon(icon, size: 30),
const SizedBox(height: 6),
Text(label),
const SizedBox(height: 4),
Text(
value,
style: const TextStyle(
fontWeight: FontWeight.bold,
),
),
],
);
}
}
37. Understanding the Complete Application
| Component | Responsibility |
| Weather | Represents weather data |
| WeatherService | Communicates with the weather API |
| WeatherPage | Manages the weather screen |
| TextEditingController | Reads the city entered by the user |
| _searchWeather() | Requests and processes weather information |
| WeatherInfoItem | Displays individual weather details |
| setState() | Updates the UI after state changes |
38. Loading, Success, and Error States
Initial State
↓
User searches
↓
Loading State
↓
┌───────────────┐
│ │
Success Error
│ │
↓ ↓
Show Weather Show Message
Handling these states makes the application easier to understand and provides useful feedback to the user.
39. Why API Calls Should Not Be Placed Directly in build()
The build() method can run many times. An API request placed directly inside build() could therefore be triggered repeatedly.
A better approach is to perform the request in an appropriate event or lifecycle method and store the resulting Future or state in a variable when needed. Flutter's official networking example specifically warns against repeatedly starting API calls from build(). :contentReference[oaicite:5]{index=5}
40. Using FutureBuilder
For simple applications, FutureBuilder can be used to build UI based on the state of a Future.
FutureBuilder(
future: weatherFuture,
builder: (context, snapshot) {
if (snapshot.hasData) {
return Text(
snapshot.data!.city,
);
}
if (snapshot.hasError) {
return Text(
'Error: ${snapshot.error}',
);
}
return const CircularProgressIndicator();
},
)
FutureBuilder can represent loading, success, and error states for asynchronous operations. :contentReference[oaicite:6]{index=6}
41. API Key Management
Many weather services require an API key. The exact authentication process depends on the selected provider.
Do not treat a secret credential embedded in a client application as fully private. For production systems, sensitive credentials should be handled according to the API provider's security recommendations and, where appropriate, protected behind a backend service.
For learning purposes, a placeholder such as the following can be used:
const String apiKey = 'YOUR_API_KEY';
42. Search by City
The simplest Weather App allows users to enter a city name.
TextField(
controller: _cityController,
decoration: const InputDecoration(
hintText: 'Enter city name',
),
)
The city is then passed to the weather service:
final weather =
await _weatherService.fetchWeather(city);
43. Search by Latitude and Longitude
More advanced weather applications can use geographic coordinates instead of a city name.
final latitude = 19.0760;
final longitude = 72.8777;
The coordinates can then be included in an API request according to the weather provider's API format.
44. Weather Icons
You can represent different weather conditions using Flutter icons or weather-specific image assets.
Icon(
Icons.wb_sunny,
size: 80,
)
A production application can map API weather-condition codes to appropriate icons.
| Condition | Example Icon |
| Clear | Icons.wb_sunny |
| Cloudy | Icons.cloud |
| Rain | Icons.water_drop |
| Storm | Icons.thunderstorm |
| Night | Icons.nightlight |
45. Responsive Weather UI
A weather application should work across different screen sizes. Avoid placing too many fixed-width elements on the screen.
LayoutBuilder(
builder: (context, constraints) {
if (constraints.maxWidth > 600) {
return const Text(
'Tablet/Desktop Layout',
);
}
return const Text(
'Mobile Layout',
);
},
)
46. Refresh Weather Data
A refresh button can allow users to request the latest weather information for the currently selected city.
IconButton(
icon: const Icon(Icons.refresh),
onPressed: _weather == null
? null
: _searchWeather,
)
47. Pull-to-Refresh
For a more advanced interface, RefreshIndicator can be used with a scrollable widget.
RefreshIndicator(
onRefresh: _searchWeather,
child: ListView(
children: [
// Weather content
],
),
)
48. Weather Forecast Feature
The application can be extended from current weather to a multi-day forecast.
Current Weather
↓
Today
↓
Tomorrow
↓
Day 3
↓
Day 4
↓
Day 5
A forecast model could contain:
- Date
- Minimum temperature
- Maximum temperature
- Weather condition
- Weather icon
- Rain probability
49. Additional Weather Features
| Feature | Description |
| Current Location | Show weather for the device's location. |
| Forecast | Display weather for upcoming days. |
| Search History | Remember previously searched cities. |
| Favorites | Allow users to save favorite cities. |
| Dark Mode | Provide a dark application theme. |
| Weather Alerts | Display important weather notifications when supported. |
| Unit Selection | Allow Celsius/Fahrenheit selection where supported. |
| Animations | Add smooth transitions between weather states. |
| Offline Cache | Show previously loaded data when appropriate. |
50. Location-Based Weather
An advanced application can request the device's location and use latitude and longitude to retrieve weather information.
Device Location
↓
Latitude + Longitude
↓
Weather API
↓
Weather Data
↓
Flutter UI
Location-based features require appropriate platform permissions and should clearly communicate to users why location access is needed.
51. Error Handling Best Practices
- Check HTTP status codes.
- Handle invalid city names.
- Handle network failures.
- Handle invalid or unexpected JSON responses.
- Handle API authentication failures.
- Handle rate limits.
- Show user-friendly error messages.
- Provide a retry option where appropriate.
- Avoid displaying raw exceptions directly to users.
52. Network Timeout
Applications can use a timeout so a request does not wait indefinitely.
final response = await http
.get(Uri.parse(url))
.timeout(
const Duration(seconds: 10),
);
You can catch a timeout and display an appropriate message.
53. Testing the Weather App
Test the application with different scenarios:
- Open the application.
- Search for a valid city.
- Verify the temperature.
- Verify the weather description.
- Verify humidity.
- Verify wind speed.
- Search for another city.
- Try an empty search.
- Try an invalid city.
- Test the application without internet access.
- Test API authentication failure handling.
- Test loading indicators.
- Test different screen sizes.
54. Debugging API Requests
When a weather request does not work, inspect:
- API URL
- API key
- Query parameters
- HTTP method
- HTTP status code
- Response body
- JSON structure
- Internet permission
- Network connection
Flutter DevTools includes a Network View that can help inspect HTTP, HTTPS, and WebSocket traffic in supported Flutter and Dart applications. :contentReference[oaicite:7]{index=7}
55. Common Mistakes
- Forgetting to add the http package.
- Forgetting Android Internet permission when required.
- Using an incorrect API URL.
- Using an invalid API key.
- Incorrectly reading JSON fields.
- Forgetting to use await.
- Calling API methods repeatedly from build().
- Not handling HTTP errors.
- Not handling network exceptions.
- Trying to display nullable weather data without checking it.
- Not disposing TextEditingController.
- Hard-coding API response assumptions without validating the response.
56. Best Practices
- Separate API logic from UI code.
- Create model classes for API responses.
- Use async/await for asynchronous operations.
- Handle loading, success, and error states.
- Validate user input.
- Keep API credentials out of source control.
- Use reusable widgets for repeated UI elements.
- Use meaningful class and variable names.
- Keep network code testable.
- Use appropriate error messages.
- Test with both successful and failed network requests.
57. Suggested Project Architecture
lib/
├── main.dart
├── models/
│ └── weather.dart
├── services/
│ └── weather_service.dart
├── screens/
│ └── weather_page.dart
├── widgets/
│ ├── weather_card.dart
│ └── weather_info_item.dart
└── utils/
└── constants.dart
58. Application Architecture Flow
Flutter UI
↓
Weather Page
↓
Weather Service
↓
HTTP Client
↓
Weather API
↓
JSON Data
↓
Weather Model
↓
Flutter UI
59. Practical Project Improvements
After completing the basic Weather App, try implementing these improvements:
- Add current-location weather.
- Add a five-day forecast.
- Add search history.
- Add favorite cities.
- Add a dark theme.
- Add weather animations.
- Add refresh functionality.
- Add Celsius/Fahrenheit selection.
- Add weather icons based on API conditions.
- Add offline caching.
- Add unit tests for the weather model.
- Add tests for the API service using mocked responses.
- Separate the application into multiple files.
60. Interview Questions
Q1. What is an API?
An API is an interface that allows one software application to communicate with another software service.
Q2. Why is the http package used?
The http package provides convenient APIs for making HTTP requests such as GET, POST, PUT, and DELETE.
Q3. What is JSON?
JSON is a structured text format commonly used for exchanging data between applications and web services.
Q4. What does jsonDecode() do?
jsonDecode() converts a JSON-formatted string into Dart data structures such as maps and lists.
Q5. Why use async and await?
They make asynchronous operations such as network requests easier to write and understand without blocking the UI thread.
Q6. What is a Future?
A Future represents a value or error that will become available after an asynchronous operation completes.
Q7. Why should API calls not normally be placed directly in build()?
The build() method can be called repeatedly, so placing an API request directly inside it can cause repeated requests.
Q8. What is FutureBuilder?
FutureBuilder is a Flutter widget that builds UI based on the current state of a Future, such as loading, success, or error.
Q9. Why create a Weather model?
A model provides a structured representation of API data and makes the application easier to maintain.
Q10. How can a Weather App display data for different cities?
The application can accept a city name, include it in the API request, parse the returned data, and update the UI.
61. Learning Outcomes
By completing this project, you gain practical experience with Flutter networking and API-driven interfaces. You learn how to collect user input, make an asynchronous HTTP request, decode JSON, transform API data into Dart objects, update application state, and display the result in a responsive interface.
62. Final Project Flow
Create Flutter Project
↓
Add HTTP Package
↓
Create Weather Model
↓
Create Weather Service
↓
Configure API
↓
Create Search Field
↓
Accept City Name
↓
Send HTTP GET Request
↓
Receive JSON Response
↓
Decode JSON
↓
Create Weather Object
↓
Update Application State
↓
Display Weather Card
↓
Handle Loading and Errors
↓
Test Application
↓
Add Forecast and Advanced Features
63. Summary
- A Weather App is an excellent project for learning Flutter API integration.
- The http package can be used to make HTTP requests.
- Weather data is commonly received in JSON format.
- jsonDecode() can be used to decode JSON data.
- Model classes make API data easier to work with.
- async and await are useful for asynchronous network operations.
- StatefulWidget and setState() can manage simple application state.
- Loading and error states should be handled properly.
- Weather API credentials should be handled securely.
- A basic app can later be expanded with forecasts, location, favorites, caching, and notifications.
64. Learn Flutter
JustAcademy Flutter Training Course
Register for Flutter Course Demo